#ifndef XG_JSON_H
#define XG_JSON_H
////////////////////////////////////////////////////////////////////
extern "C"
{
	#include "src/cJSON.h"
}

#include "../stdx/Reflect.h"

#define REGEX_NAME "[a-zA-Z_][a-zA-Z0-9_]{0,63}"
#define REGEX_MAIL "([0-9A-Za-z\\-_\\.]+)@([0-9a-z]+\\.[a-z]{2,3}(\\.[a-z]{2})?)"
#define REGEX_HOST "([0-9A-Za-z\\-_\\.]+)\\.([0-9a-z]+\\.[a-z]{2,3}(\\.[a-z]{2})?)"
#define REGEX_IPV4 "((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])"
#define REGEX_MOBILE "1[2-9][0-9]{9}"

#define REGEX_DIGIT(minlen, maxlen) "[0-9]{"#minlen","#maxlen"}"
#define REGEX_ALPHA(minlen, maxlen) "[a-z|A-Z]{"#minlen","#maxlen"}"
#define REGEX_ALNUM(minlen, maxlen) "[0-9|a-z|A-Z]{"#minlen","#maxlen"}"

class JsonElement : public Object
{
	cJSON* elem;
	sp<cJSON> holder;

	static bool InitElement(cJSON* item);
	static bool InitElement(cJSON* item, int val);
	static bool InitElement(cJSON* item, bool val);
	static bool InitElement(cJSON* item, double val);
	static bool InitElement(cJSON* item, long long val);
	static bool InitElement(cJSON* item, const char* val);
	static bool InitElement(cJSON* item, unsigned long long val);

	static bool InitElement(cJSON* item, long val)
	{
		return InitElement(item, (long long)(val));
	}
	static bool InitElement(cJSON* item, short val)
	{
		return InitElement(item, (long long)(val));
	}
	static bool InitElement(cJSON* item, float val)
	{
		return InitElement(item, (double)(val));
	}
	static bool InitElement(cJSON* item, unsigned int val)
	{
		return InitElement(item, (unsigned long long)(val));
	}
	static bool InitElement(cJSON* item, unsigned short val)
	{
		return InitElement(item, (unsigned long long)(val));
	}
	static bool InitElement(cJSON* item, unsigned long val)
	{
		return InitElement(item, (unsigned long long)(val));
	}
	static bool InitElement(cJSON* item, const string& val)
	{
		return InitElement(item, val.c_str());
	}

public:
	class iterator
	{
		friend class JsonElement;

		int idx;
		int len;
		cJSON* elem;
		sp<cJSON> holder;

		iterator(cJSON* item, const JsonElement* parent);

	public:
		iterator& operator ++ ();
		JsonElement operator * ();
		bool operator != (const iterator& obj) const;
	};

	int size() const;
	JsonElement add();
	void remove(int idx);
	string toString() const;
	string getVariable() const;
	bool load(const string& msg);
	JsonElement get(int idx) const;
	void remove(const string& name);
	JsonElement get(const string& name) const;
	bool init(const string& msg = "", function<cJSON*(void)> creator = cJSON_CreateObject);
	JsonElement addObject(const string& name, function<cJSON*(void)> creator = cJSON_CreateObject);

	JsonElement operator [] (int idx);
	JsonElement operator [] (const string& name);
	JsonElement& operator = (const JsonElement& val);

	JsonElement() : elem(NULL)
	{
	}
	JsonElement(const string& msg) : elem(NULL)
	{
		init(msg);
	}
	JsonElement(cJSON* item, const JsonElement* parent)
	{
		holder = parent ? parent->holder : NULL;
		elem = item;
	}

	iterator end() const
	{
		return iterator(NULL, this);
	}
	iterator begin() const
	{
		return iterator(elem, this);
	}
	string getName() const
	{
		return elem ? elem->string : stdx::EmptyString();
	}
	JsonElement pack(cJSON* item) const
	{
		return JsonElement(item, this);
	}

	bool isNull() const
	{
		return elem == NULL || elem->type == cJSON_NULL;
	}
	bool isBool() const
	{
		return elem && (elem->type == cJSON_True || elem->type == cJSON_False);
	}
	bool isArray() const
	{
		return elem && elem->type == cJSON_Array;
	}
	bool isObject() const
	{
		return elem && elem->type == cJSON_Object;
	}
	bool isNumber() const
	{
		return elem && elem->type == cJSON_Number;
	}
	bool isString() const
	{
		return elem && elem->type == cJSON_String;
	}
	bool isNull(int idx) const
	{
		return get(idx).isNull();
	}
	bool isBool(int idx) const
	{
		return get(idx).isBool();
	}
	bool isArray(int idx) const
	{
		return get(idx).isArray();
	}
	bool isObject(int idx) const
	{
		return get(idx).isObject();
	}
	bool isNumber(int idx) const
	{
		return get(idx).isNumber();
	}
	bool isString(int idx) const
	{
		return get(idx).isString();
	}
	bool isNull(const string& name) const
	{
		return get(name).isNull();
	}
	bool isBool(const string& name) const
	{
		return get(name).isBool();
	}
	bool isArray(const string& name) const
	{
		return get(name).isArray();
	}
	bool isObject(const string& name) const
	{
		return get(name).isObject();
	}
	bool isNumber(const string& name) const
	{
		return get(name).isNumber();
	}
	bool isString(const string& name) const
	{
		return get(name).isString();
	}

	bool getVariable(bool& val) const
	{
		CHECK_FALSE_RETURN(elem && (elem->type == cJSON_True || elem->type == cJSON_False));
		val = elem->type == cJSON_True;
		return true;
	}
	bool getVariable(int& val) const
	{
		CHECK_FALSE_RETURN(elem && elem->type == cJSON_Number);
		val = elem->valueint;
		return true;
	}
	bool getVariable(long& val) const
	{
		CHECK_FALSE_RETURN(elem && elem->type == cJSON_Number);
		val = elem->valueint;
		return true;
	}
	bool getVariable(double& val) const
	{
		CHECK_FALSE_RETURN(elem && elem->type == cJSON_Number);
		val = elem->valuedouble;
		return true;
	}
	bool getVariable(string& val) const
	{
		CHECK_FALSE_RETURN(elem && elem->valuestring);
		val = elem->valuestring;
		return true;
	}

	int asInt() const
	{
		int val = 0;
		if (getVariable(val)) return val;
		return stdx::atoi(getVariable().c_str());
	}
	long asLong() const
	{
		long val = 0;
		if (getVariable(val)) return val;
		return stdx::atol(getVariable().c_str());
	}
	bool asBool() const
	{
		string str = getVariable();
		if (str.empty()) return false;
		if (stdx::atoi(str.c_str())) return true;
		return strcasecmp(str.c_str(), "true") == 0;
	}
	double asDouble() const
	{
		double val = 0;
		if (getVariable(val)) return val;
		return stdx::atof(getVariable().c_str());
	}
	string asString() const
	{
		return getVariable();
	}
	int asInt(int idx) const
	{
		return get(idx).asInt();
	}
	long asLong(int idx) const
	{
		return get(idx).asLong();
	}
	bool asBool(int idx) const
	{
		return get(idx).asBool();
	}
	double asDouble(int idx) const
	{
		return get(idx).asDouble();
	}
	string asString(int idx) const
	{
		return get(idx).asString();
	}
	int asLong(const string& name) const
	{
		return get(name).asLong();
	}
	int asInt(const string& name) const
	{
		return get(name).asInt();
	}
	bool asBool(const string& name) const
	{
		return get(name).asBool();
	}
	double asDouble(const string& name) const
	{
		return get(name).asDouble();
	}
	string asString(const string& name) const
	{
		return get(name).asString();
	}

	JsonElement addNull(const string& name)
	{
		return addObject(name, cJSON_CreateNull);
	}
	JsonElement addArray(const string& name)
	{
		return addObject(name, cJSON_CreateArray);
	}
	template<class T> JsonElement& add(const T& val)
	{
		add() = val;
		return *this;
	}
	template<class T> bool setVariable(const T& val)
	{
		return InitElement(elem, val);
	}
	template<class T> JsonElement& operator = (const T& val)
	{
		setVariable(val);
		return *this;
	}
};

static string& operator += (string& str, const JsonElement& obj)
{
	return str += obj.toString();
}

static ostream& operator << (ostream& out, const JsonElement& obj)
{
	return out << obj.toString();
}

static StringCreator& operator << (StringCreator& out, const JsonElement& obj)
{
	return out << obj.toString();
}

static string operator + (const string& str, const JsonElement& obj)
{
	return str + obj.toString();
}

class JsonReflect : public Object
{
public:
	virtual bool isArray() const;
	virtual string toString() const;
	virtual string toDocString() const;
	virtual bool fromObject(const JsonReflect& obj);
	virtual void toJsonElement(JsonElement& dest) const;
	virtual bool fromJsonElement(const JsonElement& obj, bool inited = true);
	virtual void toJsonElementAttrs(JsonElement& dest, const vector<ReflectItem>& attrs) const;
	virtual bool fromJsonElementAttrs(const JsonElement& obj, bool inited, const vector<ReflectItem>& attrs);

	bool fromString(const string& msg)
	{
		return fromJsonElement(JsonElement(msg));
	}
};

template <class DATA_TYPE>
class JsonReflectList : public JsonReflect
{
protected:
	robject(vector<sp<DATA_TYPE>>, vec);

public:
	void clear()
	{
		vec.clear();
	}
	bool empty() const
	{
		return vec.empty();
	}
	size_t size() const
	{
		return vec.size();
	}
	sp<DATA_TYPE> add()
	{
		sp<DATA_TYPE> item = newsp<DATA_TYPE>();

		vec.push_back(item);

		return item;
	}
	sp<DATA_TYPE> add(sp<DATA_TYPE> val)
	{
		vec.push_back(val);

		return val;
	}
	sp<DATA_TYPE> add(const DATA_TYPE& val)
	{
		sp<DATA_TYPE> item = newsp<DATA_TYPE>(val);

		vec.push_back(item);
		
		return item;
	}
	sp<DATA_TYPE> get(int idx) const
	{
		return vec[idx];
	}
	const vector<sp<DATA_TYPE>>& getList() const
	{
		return vec;
	}

public:
	bool isArray() const
	{
		return true;
	}
	string toDocString() const
	{
		return DATA_TYPE().toDocString();
	}
	bool fromObject(const JsonReflect& obj)
	{
		return fromString(obj.toString());
	}
	void toJsonElement(JsonElement& dest) const
	{
		vector<ReflectItem> attrs = ReflectHelper::GetAttrList<DATA_TYPE>();

		for (auto& item : vec)
		{
			JsonReflect* data = dynamic_cast<JsonReflect*>(item.get());

			if (data)
			{
				JsonElement item = dest.add();

				data->toJsonElementAttrs(item, attrs);
			}
			else
			{
				dest.add().load(item->toString());
			}
		}
	}
	bool fromJsonElement(const JsonElement& obj, bool inited = true)
	{
		CHECK_FALSE_RETURN(obj.isArray());

		if (inited) clear();

		vector<ReflectItem> attrs = ReflectHelper::GetAttrList<DATA_TYPE>();

		for (auto item : obj)
		{
			sp<DATA_TYPE> dest = newsp<DATA_TYPE>();

			CHECK_FALSE_RETURN(dest->fromJsonElementAttrs(item, inited, attrs));

			vec.push_back(dest);
		}

		return true;
	}
};

#define DefineJsonReflectList(T, V)									\
template <> class JsonReflectList<T> : public JsonReflect			\
{                                                                   \
protected:                                                          \
	robject(vector<T>, vec);                                      	\
																	\
public:                                                             \
	void clear()                                                    \
	{                                                               \
		vec.clear();                                                \
	}                                                               \
	bool empty() const                                              \
	{                                                               \
		return vec.empty();                                         \
	}                                                               \
	bool isArray() const											\
	{																\
		return true;												\
	}																\
	void add(T val)                                               	\
	{                                                               \
		vec.push_back(val);                                         \
	}                                                               \
	size_t size() const                                             \
	{                                                               \
		return vec.size();                                          \
	}                                                               \
	T get(int idx) const                                          	\
	{                                                               \
		return vec[idx];                                            \
	}                                                               \
	const vector<T>& getList() const                             	\
	{                                                               \
		return vec;                                                 \
	}                                                               \
																	\
public:                                                             \
	string toDocString() const                                      \
	{                                                               \
		return stdx::EmptyString();                                 \
	}                                                               \
	bool fromObject(const JsonReflect& obj)                         \
	{                                                               \
		return fromString(obj.toString());                          \
	}                                                               \
	void toJsonElement(JsonElement& dest) const                     \
	{                                                               \
		for (auto item : vec) dest.add(item);                    	\
	}                                                               \
	bool fromJsonElement(const JsonElement& obj, bool inited = true)\
	{                                                               \
		CHECK_FALSE_RETURN(obj.isArray());                          \
																	\
		if (inited) clear();                                        \
																	\
		for (auto item : obj) vec.push_back(item.V());          	\
																	\
		return true;                                                \
	}                                                               \
};

DefineJsonReflectList(int, asInt)
DefineJsonReflectList(bool, asBool)
DefineJsonReflectList(long, asLong)
DefineJsonReflectList(float, asDouble)
DefineJsonReflectList(double, asDouble)
DefineJsonReflectList(string, asString)

#define JsonEntity(clazz) struct clazz : public JsonReflect

#define rarray(type, ...) robject(JsonReflectList<type>, __VA_ARGS__)
#define narray(type, ...) robject(JsonReflectList<type>, __VA_ARGS__, "optional")

////////////////////////////////////////////////////////////////////
#endif